#pragma once

#include <any>
#include <cstdint>
#include <map>
#include <memory>
#include <set>
#include <string>

class SingletonContainer {
public:
    static std::shared_ptr<SingletonContainer> GetInstance();
    SingletonContainer() { printf("%s\n", __func__); }
    ~SingletonContainer();

    void AddSingleton(const std::string &name, const std::any &instance);
    void SetSingleton(const std::string &name, const std::any &instance);
    const std::any& GetSingleton(const std::string &name);
    const std::any& DependOn(const std::string &instance, const std::string &name);

    template<class T>
    static std::shared_ptr<T> Get()
    {
        std::string nameT = __PRETTY_FUNCTION__;
        nameT = nameT.substr(nameT.find("T = "));
        nameT = nameT.substr(sizeof("T ="), nameT.length() - sizeof("T = "));
        std::shared_ptr<T> ret = nullptr;
        const std::any &instance = SingletonContainer::GetInstance()->GetSingleton(nameT);
        auto pRet = std::any_cast<std::shared_ptr<T>>(&instance);
        if (pRet != nullptr) {
            ret = *pRet;
        }
        return ret;
    }

private:
    static inline std::shared_ptr<SingletonContainer> instance = nullptr;

    struct module {
        std::any value;
        int32_t refCount;
    };
    std::map<std::string, int32_t> stringMap;
    std::map<int32_t, SingletonContainer::module> moduleMap;
    std::map<int32_t, std::set<int32_t>> dependencySetMap;
};

template<class T>
class SingletonDelegator {
public:
    SingletonDelegator()
    {
        nameT = __PRETTY_FUNCTION__;
        nameT = nameT.substr(nameT.find("T = "));
        nameT = nameT.substr(sizeof("T ="), nameT.length() - sizeof("T = "));
        SingletonContainer::GetInstance()->AddSingleton(nameT, T::GetInstance());
    }

    template<class S>
    std::shared_ptr<S> Dep()
    {
        std::string nameS = __PRETTY_FUNCTION__;
        nameS = nameS.substr(nameS.find("S = "));
        nameS = nameS.substr(sizeof("S ="), nameS.length() - sizeof("S = "));

        auto ret = SingletonContainer::Get<S>();
        if (ret != nullptr) {
            SingletonContainer::GetInstance()->DependOn(nameT, nameS);
        }
        return ret;
    }

private:
    std::string nameT;
};
